그룹스터디 후기 기능 추가 및 스터디 수정 모달 관련 로직 수정#444
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
└ MENTOR_STUDY→PREMIUM_STUDY 역매핑, thumbnailExtension 안전 가드 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthrough마이 스터디 리뷰 기능을 새로 추가하고 관련 레이아웃, 모달, 훅, 쿼리, 타입 및 그룹/멘토/1:1 리뷰 페이지와 통계·평가 UI를 도입했습니다. 일부 스터디 리스트 페이지와 그룹 스터디 폼/스키마도 관련 로직으로 함께 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Client as Client(React)
participant API as Backend API
participant DB as Database
User->>Client: /my-study-review/group 접속
Client->>API: 완료된 스터디 목록 요청 (useMemberStudyListQuery)
API->>DB: 사용자 스터디 조회
DB-->>API: 스터디 목록 반환
API-->>Client: 스터디 목록 전달
Client->>API: 각 스터디별 작성여부 병렬 요청 (groupStudyReviewQueryKeys.written)
API->>DB: 리뷰 작성 여부 조회
DB-->>API: 작성여부 응답
API-->>Client: 작성여부 반환
Client->>Client: 클릭 가능성 판단(리더/이미 작성 등)
User->>Client: 작성 가능한 스터디 클릭
Client->>API: 그룹 스터디 상세 및 선택 항목 요청
API->>DB: 상세/선택항목 조회
DB-->>API: 상세정보 반환
API-->>Client: 상세정보 전달
Client->>Client: GroupStudyReviewModal 표시
User->>Client: 리뷰 입력 및 제출
Client->>API: 리뷰 생성 요청 (useCreateGroupStudyReview)
API->>DB: 리뷰 저장
DB-->>API: 저장 결과
API-->>Client: 성공 응답
Client->>Client: 모달 닫고 StudyCompletionModal 오픈 (confetti)
sequenceDiagram
actor User
participant Client as Client(React)
participant API as Backend API
participant DB as Database
User->>Client: /my-study-review/group/[groupStudyId] 접속
Client->>API: 그룹 스터디 상세 조회
API->>DB: 스터디 정보 조회
DB-->>API: 스터디 정보 반환
API-->>Client: 상세정보 전달
Client->>API: 리뷰 목록 조회 (page=1,size=100)
API->>DB: 리뷰 목록 조회
DB-->>API: 목록 반환
API-->>Client: 리뷰 목록 전달
Client->>Client: 리뷰 ID 추출
Client->>API: 각 리뷰 상세 병렬 조회 (useQueries)
API->>DB: 상세 조회(다건)
DB-->>API: 상세 반환
API-->>Client: 모든 상세 정보 전달
Client->>Client: useReviewStatistics로 통계 계산 및 UI 렌더링
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
🟡 Minor comments (5)
src/app/(service)/(my)/my-study-review/_components/use-expandable-content.tsx-8-16 (1)
8-16:⚠️ Potential issue | 🟡 Minor
content변경 시showButton상태가 업데이트되지 않습니다.
useEffect의 의존성 배열이 비어있어 컴포넌트 마운트 시에만 실행됩니다.content가 동적으로 변경되면 버튼 표시 여부가 재계산되지 않아 UI가 올바르게 업데이트되지 않을 수 있습니다.🐛 의존성 배열에 content 추가 제안
useEffect(() => { if (contentRef.current) { const lineHeight = parseInt(window.getComputedStyle(contentRef.current).lineHeight, 10) || 20; const maxHeight = lineHeight * 3; setShowButton(contentRef.current.scrollHeight > maxHeight); } - }, []); + }, [content]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/use-expandable-content.tsx around lines 8 - 16, The effect that calculates whether to show the expand button (inside useEffect using contentRef, calculating lineHeight and maxHeight and calling setShowButton) only runs on mount because its dependency array is empty; update the dependency array to include the dynamic value(s) that should retrigger recalculation—at minimum include content (and optionally contentRef.current if needed) so that when content changes the effect reruns and setShowButton updates accordingly. Ensure the useEffect references remain correct (contentRef, setShowButton, and any helpers used to compute lineHeight) so React linter warnings are satisfied.src/app/(service)/(my)/my-study-review/_components/review-page-content.tsx-145-149 (1)
145-149:⚠️ Potential issue | 🟡 Minor
onClick에fetchNextPage를 직접 전달하는 대신 화살표 함수로 감싸세요.
useInfiniteQuery에서 반환되는fetchNextPage를 직접 전달하면 클릭 이벤트 객체가 함수의 첫 번째 인자로 전달될 수 있습니다. 프로젝트 코드베이스의 다른 곳(예:one-to-one/page.tsx라인 65)에서 이미onClick={() => fetchNextPage()}로 올바르게 구현하고 있으므로 동일한 패턴을 적용해야 합니다.제안 수정
- onClick={fetchNextPage} + onClick={() => fetchNextPage()}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/review-page-content.tsx around lines 145 - 149, The onClick currently passes fetchNextPage directly which may receive the click event as its first argument; change the handler to an arrow function so it calls fetchNextPage without passing the event (i.e., replace onClick={fetchNextPage} with onClick={() => fetchNextPage()}) on the button that is rendered when hasNextPage is true to match the pattern used elsewhere (e.g., one-to-one/page.tsx) and avoid unintended arguments being forwarded.src/components/common/modals/group-study-review-modal.tsx-235-254 (1)
235-254:⚠️ Potential issue | 🟡 Minor만족도 토글의 선택 상태를 보조기기에 알려주세요.
지금은 시각적으로만 활성 상태가 바뀌어서 스크린리더가 현재 선택을 읽어주지 못합니다. 최소한
aria-pressed={isSelected}를 넣거나 라디오 그룹 semantics로 바꾸는 편이 안전합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/modals/group-study-review-modal.tsx` around lines 235 - 254, The visual-only toggle button in group-study-review-modal.tsx needs accessible state so screen readers announce selection; update the button element that uses isSelected/onClick/label/emoji to include an ARIA state such as aria-pressed={isSelected} (or switch to proper radio-group semantics if this is part of a mutually exclusive set), and ensure the element has an appropriate role/keyboard behavior if you change semantics; add aria-pressed to the existing <button> for the quickest fix.src/components/common/modals/study-completion-modal.tsx-45-74 (1)
45-74:⚠️ Potential issue | 🟡 Minor닫힐 때
requestAnimationFrame루프를 취소하지 않고 있습니다.캔버스를 제거해도 이미 예약된 프레임은 3초 동안 계속 돌아갑니다. 빠르게 열고 닫으면 불필요한 애니메이션 루프가 겹칠 수 있습니다.
수정 예시
- const frame = () => { - if (Date.now() > end) return; + let rafId: number | null = null; + let cancelled = false; + + const frame = () => { + if (cancelled || Date.now() > end) return; @@ - requestAnimationFrame(frame); + rafId = requestAnimationFrame(frame); }; @@ return () => { + cancelled = true; + if (rafId !== null) cancelAnimationFrame(rafId); if (canvasRef.current) { canvasRef.current.remove(); canvasRef.current = null; } confettiRef.current = null;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/modals/study-completion-modal.tsx` around lines 45 - 74, The frame animation loop created by function frame uses requestAnimationFrame but never cancels it on unmount, causing lingering loops; fix by storing the requestAnimationFrame id (e.g., in confettiRef or a new rafRef) each time you call requestAnimationFrame(frame), check a cancellation flag or confettiRef before scheduling the next frame, and call cancelAnimationFrame(storedId) in the cleanup return block (along with removing the canvas and clearing refs) so the loop is properly stopped when the modal unmounts or the canvas is removed.src/app/(service)/(my)/my-study-review/_components/completed-study-review-page.tsx-91-97 (1)
91-97:⚠️ Potential issue | 🟡 Minor
memberId가null일 때 쿼리가 실행될 수 있습니다.
memberId ?? 0으로 0을 전달하면 쿼리가 실행되어 잘못된 데이터를 요청할 수 있습니다.useMemberStudyListQuery에enabled조건을 추가하거나,memberId가 없을 때 early return을 고려해주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx around lines 91 - 97, The query may run with memberId null because we pass memberId ?? 0 to useMemberStudyListQuery; update the call to avoid executing when memberId is falsy by adding an enabled flag (e.g. enabled: Boolean(memberId)) or by returning early when memberId is null/undefined before calling useMemberStudyListQuery; refer to useMemberStudyListQuery, memberId, and completedStudyResponse to locate and gate the query.
🧹 Nitpick comments (14)
src/components/common/ui/star-rating-input.tsx (1)
13-19:roundToNearestHalf함수의 불필요한 조건 분기
num === 0일 때의 특별 처리가 불필요합니다.Math.round(0 * 2) / 2는 항상0을 반환하므로,.toFixed(1)을 적용하고Number()로 변환해도 결과는 동일합니다.♻️ 간소화된 구현 제안
const roundToNearestHalf = (num: number) => { - if (num === 0) { - return Number((Math.round(num * 2) / 2).toFixed(1)); - } - return Math.round(num * 2) / 2; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/ui/star-rating-input.tsx` around lines 13 - 19, The special-case branch in roundToNearestHalf is unnecessary; remove the if (num === 0) block and simplify the function to always return Math.round(num * 2) / 2 so zero and other values are handled uniformly; update the function roundToNearestHalf to a single return expression and ensure callers still expect a number (no toFixed/Number conversion).src/app/(service)/(my)/my-study-review/_components/review-page-content.tsx (1)
11-11: 상대 경로 import 대신@/alias로 맞춰 주세요.이 훅도
@/app/(service)/(my)/my-study-review/_components/use-expandable-content처럼 절대 경로로 통일하는 편이 좋겠습니다.As per coding guidelines,
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/review-page-content.tsx at line 11, Replace the relative import of the hook useExpandableContent (import { useExpandableContent } from './use-expandable-content') with the project alias form that begins with '@/...' so it matches the tsconfig alias convention; update the import statement to use the alias-prefixed module path (keeping the same exported symbol useExpandableContent) to make imports consistent with other components.src/app/(service)/(my)/my-study-review/group/page.tsx (1)
3-3: 신규 페이지 import도@/alias로 맞춰 주세요.이 경로는
@/app/(service)/(my)/my-study-review/_components/completed-study-review-page로 통일하는 편이 좋겠습니다.As per coding guidelines,
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/page.tsx at line 3, The import in page.tsx uses a relative path for CompletedStudyReviewPage; update the import to use the project path alias by replacing the relative import with "@/app/(service)/(my)/my-study-review/_components/completed-study-review-page" so CompletedStudyReviewPage is imported via the `@/` alias (adjust the import statement in page.tsx to reference that module).src/app/(service)/(my)/my-study-review/_components/study-review-tab-nav.tsx (1)
19-48: 활성 탭 상태를 보조기기에 노출해 주세요.지금은 활성 여부가 스타일로만 표현돼서 스크린 리더가 현재 페이지를 알기 어렵습니다. active 항목에 현재 페이지를 나타내는 속성(예:
aria-current="page")을 추가해 두는 편이 좋겠습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/study-review-tab-nav.tsx around lines 19 - 48, The three tab buttons in the StudyReviewTabNav component (the buttons that call router.push(...) for '/my-study-review/one-to-one', '/my-study-review/group', '/my-study-review/mentor' and use cn(tabClassName, isActive(...) && pathClassName)) rely only on visual styling to show the active state; update each button to include an accessibility attribute when active (e.g., add aria-current="page" when isActive('/my-study-review/...') is true) so screen readers can detect the current page—use the existing isActive(...) check for each button to conditionally set aria-current.src/app/(service)/(my)/my-study-review/mentor/page.tsx (1)
3-3: 여기도@/alias import로 통일해 주세요.
../_components/completed-study-review-page보다 절대 경로 import가 유지보수에 더 안정적입니다.As per coding guidelines,
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/mentor/page.tsx at line 3, The import for CompletedStudyReviewPage currently uses a relative path; update the import to use the project alias (`@/`) mapped to ./src/ to follow the Component path aliases guideline. Replace the line importing CompletedStudyReviewPage (import CompletedStudyReviewPage from '../_components/completed-study-review-page') with an alias import that starts with "@/..." pointing to the same module, ensuring the symbol CompletedStudyReviewPage and the module name completed-study-review-page remain unchanged so other references continue to work. Verify TypeScript resolves the alias (tsconfig paths) and run a quick build/type-check to confirm no import errors.src/app/(service)/(my)/my-study-review/one-to-one/_components/one-to-one-review-card.tsx (1)
7-7: 상대 경로 import 대신@/alias로 통일해 주세요.
../../_components/use-expandable-content보다@/app/(service)/(my)/my-study-review/_components/use-expandable-content가 이동/리팩터링에 더 안전합니다.As per coding guidelines,
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/one-to-one/_components/one-to-one-review-card.tsx at line 7, Replace the relative import of useExpandableContent with the project alias: update the import statement that currently references '../../_components/use-expandable-content' in one-to-one-review-card.tsx to use the alias module '@/app/(service)/(my)/my-study-review/_components/use-expandable-content' and keep the imported symbol name useExpandableContent unchanged so tooling and refactors resolve correctly.src/components/forms/group-study-form.tsx (1)
24-25: context와 hook을 전용 모듈로 분리하세요.
step1-group.tsx가 이 파일에서useMode를 가져오면서 순환 의존이 발생합니다:group-study-form.tsx→step1-group.tsx→group-study-form.tsx.useMode와useClassification을 별도 파일(예:src/contexts/group-study-mode.ts)로 추출하면 이 의존성을 제거하고 초기화 순서와 HMR 안정성을 개선할 수 있습니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/forms/group-study-form.tsx` around lines 24 - 25, Extract the ModeContext and useMode (and useClassification if present) into a dedicated module (e.g., a new src/contexts/group-study-mode.ts) so they are no longer defined inside group-study-form.tsx; move the createContext<'create' | 'edit'>('create') declaration and the export const useMode hook into that new file, export both the context and hook, then update group-study-form.tsx and step1-group.tsx to import ModeContext/useMode (and useClassification) from the new module; ensure you remove the original declarations from group-study-form.tsx to break the circular dependency and keep the same exported identifiers (ModeContext, useMode, useClassification) so callers need minimal changes.src/components/common/modals/group-study-review-modal.tsx (1)
56-56: 금지된 arbitrary Tailwind 값이 여러 곳에 들어가 있습니다.
max-w-[360px],h-[120px],h-[52px],w-[52px],text-[28px]는 저장소 규칙상 토큰 클래스로 치환해야 합니다.As per coding guidelines
Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens.Also applies to: 174-174, 245-250
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/modals/group-study-review-modal.tsx` at line 56, Replace all forbidden arbitrary Tailwind utilities (max-w-[360px], h-[120px], h-[52px], w-[52px], text-[28px]) with the project's design-token classes; locate the usages in the JSX (e.g., the Modal.Title element and the avatar/box elements that currently use those bracketed utilities) and swap each arbitrary value for the equivalent token class (or add a new design token if none exists), updating the className strings accordingly so only approved token classes remain.src/app/(service)/(my)/my-study-review/group/_components/evaluation-section.tsx (1)
30-30:min-h-[280px]는 토큰 클래스로 바꿔주세요.저장소 규칙상 arbitrary Tailwind 값은 허용되지 않습니다.
As per coding guidelines
Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/_components/evaluation-section.tsx at line 30, The div in evaluation-section.tsx uses an arbitrary Tailwind class min-h-[280px] which violates the project's rule against arbitrary values; replace that class with the appropriate project design token class (the component's root div with className "rounded-100 border-border-subtle min-h-[280px] border p-200") — find the matching min-height token from the design token token set (or use the existing project token naming convention, e.g., min-h-XXX) and update the className to use that token instead of min-h-[280px].src/components/common/modals/study-completion-modal.tsx (1)
53-60: 테마 토큰 대신 하드코딩된 값이 들어가 있습니다.여기의 hex 색상들과
text-[64px]는 저장소 규칙과 맞지 않습니다. CSS 변수나 프로젝트 토큰을 읽어서 쓰는 쪽이 테마 변경에도 안전합니다.As per coding guidelines
Forbidden: Hard-coded colors and spacing values. Use only@themeinline tokens from global.css and custom Tailwind classes.Also applies to: 88-88
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/modals/study-completion-modal.tsx` around lines 53 - 60, The confetti color hexes and the hardcoded `text-[64px]` in the StudyCompletionModal are forbidden; locate the confetti invocations (calls to fire(...) with the colors: [...] arrays) and replace the hard-coded hex values with the theme color tokens or CSS variables exposed in global.css (use the same token names your project uses for primary/accent/grape/amber/green), and replace `text-[64px]` with the appropriate project Tailwind/custom class (or an `@theme` inline token class) that maps to the 64px size; ensure you read tokens from the global theme (or use var(--your-token)) rather than literals so theme changes apply consistently.src/hooks/queries/group-study-review-api.ts (1)
130-136: 순차적 캐시 무효화를 병렬로 처리하면 성능이 개선됩니다.현재
await를 순차적으로 호출하고 있어 두 무효화 작업이 직렬로 실행됩니다.Promise.all로 병렬 처리하면 약간의 성능 향상이 가능합니다.♻️ 병렬 무효화 제안
onSuccess: async (_, variables) => { - await queryClient.invalidateQueries({ - queryKey: groupStudyReviewQueryKeys.lists(), - }); - await queryClient.invalidateQueries({ - queryKey: groupStudyReviewQueryKeys.written(variables.groupStudyId), - }); + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: groupStudyReviewQueryKeys.lists(), + }), + queryClient.invalidateQueries({ + queryKey: groupStudyReviewQueryKeys.written(variables.groupStudyId), + }), + ]); },Also applies to: 154-161
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/queries/group-study-review-api.ts` around lines 130 - 136, In the onSuccess callback where you currently await queryClient.invalidateQueries twice in sequence (using groupStudyReviewQueryKeys.lists() and groupStudyReviewQueryKeys.written(variables.groupStudyId)), run both invalidations in parallel with Promise.all to avoid serial waits; update the onSuccess handler that calls queryClient.invalidateQueries with groupStudyReviewQueryKeys.lists() and groupStudyReviewQueryKeys.written(...) to invoke them inside Promise.all, and make the same change for the similar block referenced around lines 154-161.src/app/(service)/(my)/my-study-review/_components/completed-study-review-page.tsx (3)
8-8: 다이나믹 임포트 패턴이 일관되지 않습니다.
StudyCompletionModal과StudyReviewModal은dynamic()으로 지연 로딩하지만,GroupStudyReviewModal은 직접 임포트합니다. 모달의 크기와 사용 빈도가 유사하다면 일관된 패턴을 적용하는 것이 좋습니다.♻️ 일관된 다이나믹 임포트 적용
-import GroupStudyReviewModal from '@/components/common/modals/group-study-review-modal'; +const GroupStudyReviewModal = dynamic( + () => import('@/components/common/modals/group-study-review-modal'), + { ssr: false }, +);Also applies to: 17-25
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx at line 8, The import of GroupStudyReviewModal is inconsistent with the lazy/dynamic pattern used for StudyCompletionModal and StudyReviewModal; replace the direct import of GroupStudyReviewModal with a dynamic import using the same dynamic() helper used for StudyCompletionModal and StudyReviewModal so all modals are loaded lazily and consistently; update any references in the component (e.g., CompletedStudyReviewPage render logic) to use the dynamically imported GroupStudyReviewModal identifier and ensure props and default export usage remain unchanged.
58-65:studyId는 필수 필드이므로 nullish coalescing이 불필요합니다.
MemberStudyItem타입에서studyId: number는 필수 필드입니다 (context snippet 1 참조).study.studyId ?? index대신study.studyId만 사용해도 됩니다.♻️ 간소화 제안
{studies.map((study, index) => ( <MemberStudyCard - key={study.studyId ?? index} + key={study.studyId} study={study}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx around lines 58 - 65, The key for the mapped MemberStudyCard should use the required studyId instead of a fallback; update the studies.map callback so the key is study.studyId (remove the nullish coalescing `?? index`) — locate the mapping that renders <MemberStudyCard ... /> in the studies.map, and replace the key expression `study.studyId ?? index` with `study.studyId` to rely on the required field defined on MemberStudyItem.
110-125: 쿼리 키 불일치로 캐시 무효화가 누락됩니다.이 컴포넌트는
['study-review', 'written', studyType, study.studyId]쿼리 키를 사용하지만,useCreateGroupStudyReview는groupStudyReviewQueryKeys.written(groupStudyId)를 무효화합니다 (src/hooks/queries/group-study-review-api.ts참조). 이로 인해 후기 작성 후에도 이 컴포넌트의 eligibility 체크가 갱신되지 않습니다.
submittedStudyIds상태로 세션 내에서는 해결되지만, 다른 탭이나 새로고침 시에는 캐시 불일치가 발생할 수 있습니다.♻️ 권장 수정: 기존 쿼리 키 패턴 사용
+import { groupStudyReviewQueryKeys } from '@/hooks/queries/group-study-review-api'; const writtenResults = useQueries({ queries: participantStudies.map((study) => ({ - queryKey: ['study-review', 'written', studyType, study.studyId], + queryKey: studyType === 'ONE_ON_ONE_STUDY' + ? ['study-review', 'written', studyType, study.studyId] + : groupStudyReviewQueryKeys.written(study.studyId), queryFn: async () => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx around lines 110 - 125, The hardcoded queryKey array in the writtenResults useQueries call causes cache mismatches with the invalidation performed by useCreateGroupStudyReview; change the queryKey logic to use the shared query-key generators instead of a custom array — i.e., for group studies use groupStudyReviewQueryKeys.written(study.studyId) and for one-on-one studies use the corresponding one-on-one review query key generator (import the existing key helpers used elsewhere) so that invalidation from useCreateGroupStudyReview actually refreshes writtenResults; update imports to bring in groupStudyReviewQueryKeys (and the one-on-one key helper) and replace the queryKey assignment inside the map accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/app/`(service)/(my)/layout.tsx:
- Around line 20-21: The div in layout.tsx is using Tailwind arbitrary values
pb-[100px] and max-w-[780px], which violates the design-token rule; replace
pb-[100px] with an existing spacing token class (e.g., pb-400) or add a new
spacing token in global.css and use that token's class, and replace
max-w-[780px] with a custom max-width utility defined in your design tokens
(e.g., add a max-w-<token> in global.css and use that class) so both pb and
max-w use project-approved tokens instead of arbitrary values.
In
`@src/app/`(service)/(my)/my-study-review/group/_components/evaluation-section.tsx:
- Around line 88-93: The variable goodItems is inferred as any[] which causes
implicit any in the reduce callback; explicitly type goodItems (and similarly
disappointedItems if needed) as GroupStudyReviewStatisticsItem[] so the reduce
parameters are correctly typed. Locate the declaration of goodItems (and
disappointedItems) in evaluation-section.tsx and replace the inferred-typed
initialization with either a type assertion or an explicit type annotation using
GroupStudyReviewStatisticsItem[] so goodTotalCount's reduce callback no longer
uses implicit any.
In
`@src/app/`(service)/(my)/my-study-review/group/_components/satisfaction-section.tsx:
- Around line 71-86: The two arbitrary Tailwind width utilities (sm:w-[88px] and
sm:w-[96px]) in the satisfaction-section.tsx JSX violate the project guideline;
replace them with the project's custom spacing tokens defined in global.css
(e.g., the --spacing-* tokens) or add new tokens if no existing size matches.
Locate the small-screen width uses on the div containing "좋았어요" and the trailing
span that shows "{goodPercent}% ({goodCount}/{totalCount})" and swap the inline
arbitrary widths for the appropriate token-backed utility classes (or create new
--spacing-XXX tokens and corresponding utility classes) so sizes come from the
token system rather than hardcoded pixel values.
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_hooks/use-review-statistics.ts:
- Around line 73-76: The filter callback for building allItems is causing TS7006
because the parameter item lacks an explicit type; update the lambda used in
reviewDetails.flatMap to annotate item with the correct element type from
selectableReviewItems (e.g., SelectableReviewItem or the actual interface used
for review items), such as (item: SelectableReviewItem | undefined) => ..., and
keep the same guard (check item?.id) so the filter still excludes
falsy/undefined items; locate the allItems declaration and the
selectableReviewItems usages to apply this typed parameter.
In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/page.tsx:
- Around line 33-37: The current call to useGetGroupStudyReviews hardcodes page:
1 and size: 100 so reviewsData (and derived list/detail/statistics) is truncated
when totalElements > 100; change to a paginated or infinite-load approach:
replace the single static call with a state-driven paginator (e.g., page, size
state and a loadMore or onPageChange handler) that calls useGetGroupStudyReviews
with the current page and merges/appends results, or implement a looped fetch to
retrieve all pages using totalElements/totalPages returned by the hook; update
any consumers that read reviewsData to use the aggregated list (or current page)
and ensure groupStudyId is still passed through to useGetGroupStudyReviews.
- Around line 46-54: The code filters out in-flight or failed detail queries
(detailQueries -> reviewDetails) and immediately passes the truncated list into
useReviewStatistics, causing statistics to shrink and permanently exclude failed
items; instead detect whether all detailQueries are settled (useQueries results'
isLoading/isError/isFetched flags) and only call useReviewStatistics with the
full set of detail results once every query is settled, otherwise render/return
a loading or partial state (or call useReviewStatistics with the original
reviews list) so that loading and error states are separated from computed
statistics; update the logic around detailQueries, reviewDetails and the
useReviewStatistics invocation (and consider using reviewIds and
groupStudyReviewDetailQueryOptions to rebuild options) so failures are not
silently dropped during aggregation.
- Around line 39-44: The reviewsData access uses wrong property names and lets
types fall back to any; update code to use the correct response properties and
an explicit type: replace reviewsData?.content with reviewsData?.items and
reviewsData?.totalElements with reviewsData?.totalCount, and declare reviews
with the explicit type GroupStudyExperienceReviewListItem[] so reviewIds
mapping/filtering (and downstream rendering callbacks) have proper types; adjust
any references to reviews/reviewIds/totalElements accordingly to match these
corrected symbols.
In `@src/app/`(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsx:
- Around line 33-37: The reviews fetch currently hardcodes a single-page request
(useGetGroupStudyReviews called with page: 1, size: 100), causing listings,
details and stats to be truncated when totalElements > 100; change the logic to
fully load all reviews — either by iterating pages until you have fetched
totalElements (call useGetGroupStudyReviews repeatedly or enhance the hook to
accept a size based on the initial response) or by first fetching metadata
(totalElements) and then requesting with size=totalElements; update the code
around the useGetGroupStudyReviews call and any consumers of reviewsData (e.g.,
listings, detail and stats renderers) to use the combined/full result instead of
only the first page.
- Around line 46-54: 현재 reviewDetails에서 미완료/실패 항목을 제외하고 즉시 useReviewStatistics를
호출해 부분 집계가 로딩 중이거나 실패한 항목을 반영하지 못하고 있습니다; 변경할 것: detailQueries 배열의
상태(detailQueries.every(q => q.isSuccess), any isLoading, any isError)를 검사해 모든 상세
쿼리가 성공일 때만 reviewDetails (타입: GroupStudyExperienceReviewDetail)를 생성하고
useReviewStatistics를 호출하도록 하고, 하나라도 로딩이면 통계 대신 별도 로딩 UI를, 하나라도 에러면 에러 상태(또는 실패
항목에 대한 처리)를 표시하도록 변경하세요; 관련 식별자: detailQueries, reviewDetails,
useReviewStatistics, GroupStudyExperienceReviewDetail,
reviewIds.map(groupStudyReviewDetailQueryOptions).
- Around line 12-14: The imports for EvaluationSection, GroupReviewCard, and
SatisfactionSection in page.tsx point to a non-existent sibling under
[groupStudyId]; update each import to use the project alias (start with "@/")
and the actual shared folder (e.g., "@/app/(service)/(my)/group/_components/…")
so they resolve to the shared group/_components location; modify the import
statements that reference '../../group/[groupStudyId]/_components/...' to use
the '@/...' paths for EvaluationSection, GroupReviewCard, and
SatisfactionSection.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx:
- Around line 16-36: The page currently falls back to empty arrays/0 for
positiveData, negativeData and reviewData which makes loading/errors look like
"0 reviews"; update the component to surface loading and error states from
useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery and
useMyReviewsInfinityQuery before computing statistics or rendering
EvaluationSection and the reviews empty state: check each query's
isLoading/isFetching and isError/error and render inline loaders or error
messages (per guidelines: inline error first, optional Toast secondary) instead
of proceeding to buildEvaluationStatistics or using reviews/totalCount
fallbacks; only compute statistics and render EvaluationSection/reviews list
when the relevant queries have succeeded.
In `@src/components/common/modals/group-study-form-modal.tsx`:
- Around line 320-335: The edit modal still forces the start-date min to
"tomorrow" because GroupStudyForm doesn't forward the edit mode or a minDate to
the Step1OpenGroupStudy component; update GroupStudyForm (the component rendered
with methods={createMethods} and methods={editMethods}) to accept and forward a
mode prop (or compute/passthrough a minDate) into Step1OpenGroupStudy, and
update step1-group.tsx (Step1OpenGroupStudy) to use the passed mode/minDate (or
the existing default startDate when mode === 'edit') when computing the date
input's min instead of always using tomorrow so the edit flow can select
today/past existing start dates.
In `@src/components/common/modals/study-completion-modal.tsx`:
- Line 3: The build fails because StudyCompletionModal imports canvas-confetti
(import confetti from 'canvas-confetti';) but the dependency is missing from
package.json; add canvas-confetti to dependencies in package.json (pick a stable
version or run npm install --save canvas-confetti or yarn add canvas-confetti to
update package.json and lockfile), commit the updated package.json and lockfile,
and rebuild so the StudyCompletionModal can be bundled correctly.
In `@src/components/forms/group-study-steps/step1-group.tsx`:
- Around line 69-72: The current startDateMin computation forces min to
today/tomorrow even in edit mode, which invalidates an existing past startDate;
update the logic that computes startDateMin (the variable using mode,
formatKoreaYMD, addDays, getKoreaDate) so that in 'create' mode it remains
tomorrow, but in edit mode it uses the earlier of the current date and the
preloaded startDate (i.e., if the form's existing startDate is before today, use
that existing value as min) — locate where startDateMin is calculated and
replace the fixed-today branch with a comparison between getKoreaDate() (or
addDays for create) and the form's current startDate value so the existing past
date remains selectable.
In `@src/hooks/common/use-group-study-review-form.ts`:
- Around line 116-140: The handleSubmit function calls createReview with only
onSuccess; add an onError handler to the createReview call so failures surface
to the user: inside the createReview options object, implement onError to call
showToast with an error message (and optionally log the error) and ensure any UI
state cleanup (e.g., closing modal or leaving form state) is handled
appropriately; update the createReview invocation in
use-group-study-review-form.ts (the createReview call inside handleSubmit) to
include this onError callback.
---
Minor comments:
In
`@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx:
- Around line 91-97: The query may run with memberId null because we pass
memberId ?? 0 to useMemberStudyListQuery; update the call to avoid executing
when memberId is falsy by adding an enabled flag (e.g. enabled:
Boolean(memberId)) or by returning early when memberId is null/undefined before
calling useMemberStudyListQuery; refer to useMemberStudyListQuery, memberId, and
completedStudyResponse to locate and gate the query.
In `@src/app/`(service)/(my)/my-study-review/_components/review-page-content.tsx:
- Around line 145-149: The onClick currently passes fetchNextPage directly which
may receive the click event as its first argument; change the handler to an
arrow function so it calls fetchNextPage without passing the event (i.e.,
replace onClick={fetchNextPage} with onClick={() => fetchNextPage()}) on the
button that is rendered when hasNextPage is true to match the pattern used
elsewhere (e.g., one-to-one/page.tsx) and avoid unintended arguments being
forwarded.
In
`@src/app/`(service)/(my)/my-study-review/_components/use-expandable-content.tsx:
- Around line 8-16: The effect that calculates whether to show the expand button
(inside useEffect using contentRef, calculating lineHeight and maxHeight and
calling setShowButton) only runs on mount because its dependency array is empty;
update the dependency array to include the dynamic value(s) that should
retrigger recalculation—at minimum include content (and optionally
contentRef.current if needed) so that when content changes the effect reruns and
setShowButton updates accordingly. Ensure the useEffect references remain
correct (contentRef, setShowButton, and any helpers used to compute lineHeight)
so React linter warnings are satisfied.
In `@src/components/common/modals/group-study-review-modal.tsx`:
- Around line 235-254: The visual-only toggle button in
group-study-review-modal.tsx needs accessible state so screen readers announce
selection; update the button element that uses isSelected/onClick/label/emoji to
include an ARIA state such as aria-pressed={isSelected} (or switch to proper
radio-group semantics if this is part of a mutually exclusive set), and ensure
the element has an appropriate role/keyboard behavior if you change semantics;
add aria-pressed to the existing <button> for the quickest fix.
In `@src/components/common/modals/study-completion-modal.tsx`:
- Around line 45-74: The frame animation loop created by function frame uses
requestAnimationFrame but never cancels it on unmount, causing lingering loops;
fix by storing the requestAnimationFrame id (e.g., in confettiRef or a new
rafRef) each time you call requestAnimationFrame(frame), check a cancellation
flag or confettiRef before scheduling the next frame, and call
cancelAnimationFrame(storedId) in the cleanup return block (along with removing
the canvas and clearing refs) so the loop is properly stopped when the modal
unmounts or the canvas is removed.
---
Nitpick comments:
In
`@src/app/`(service)/(my)/my-study-review/_components/completed-study-review-page.tsx:
- Line 8: The import of GroupStudyReviewModal is inconsistent with the
lazy/dynamic pattern used for StudyCompletionModal and StudyReviewModal; replace
the direct import of GroupStudyReviewModal with a dynamic import using the same
dynamic() helper used for StudyCompletionModal and StudyReviewModal so all
modals are loaded lazily and consistently; update any references in the
component (e.g., CompletedStudyReviewPage render logic) to use the dynamically
imported GroupStudyReviewModal identifier and ensure props and default export
usage remain unchanged.
- Around line 58-65: The key for the mapped MemberStudyCard should use the
required studyId instead of a fallback; update the studies.map callback so the
key is study.studyId (remove the nullish coalescing `?? index`) — locate the
mapping that renders <MemberStudyCard ... /> in the studies.map, and replace the
key expression `study.studyId ?? index` with `study.studyId` to rely on the
required field defined on MemberStudyItem.
- Around line 110-125: The hardcoded queryKey array in the writtenResults
useQueries call causes cache mismatches with the invalidation performed by
useCreateGroupStudyReview; change the queryKey logic to use the shared query-key
generators instead of a custom array — i.e., for group studies use
groupStudyReviewQueryKeys.written(study.studyId) and for one-on-one studies use
the corresponding one-on-one review query key generator (import the existing key
helpers used elsewhere) so that invalidation from useCreateGroupStudyReview
actually refreshes writtenResults; update imports to bring in
groupStudyReviewQueryKeys (and the one-on-one key helper) and replace the
queryKey assignment inside the map accordingly.
In `@src/app/`(service)/(my)/my-study-review/_components/review-page-content.tsx:
- Line 11: Replace the relative import of the hook useExpandableContent (import
{ useExpandableContent } from './use-expandable-content') with the project alias
form that begins with '@/...' so it matches the tsconfig alias convention;
update the import statement to use the alias-prefixed module path (keeping the
same exported symbol useExpandableContent) to make imports consistent with other
components.
In `@src/app/`(service)/(my)/my-study-review/_components/study-review-tab-nav.tsx:
- Around line 19-48: The three tab buttons in the StudyReviewTabNav component
(the buttons that call router.push(...) for '/my-study-review/one-to-one',
'/my-study-review/group', '/my-study-review/mentor' and use cn(tabClassName,
isActive(...) && pathClassName)) rely only on visual styling to show the active
state; update each button to include an accessibility attribute when active
(e.g., add aria-current="page" when isActive('/my-study-review/...') is true) so
screen readers can detect the current page—use the existing isActive(...) check
for each button to conditionally set aria-current.
In
`@src/app/`(service)/(my)/my-study-review/group/_components/evaluation-section.tsx:
- Line 30: The div in evaluation-section.tsx uses an arbitrary Tailwind class
min-h-[280px] which violates the project's rule against arbitrary values;
replace that class with the appropriate project design token class (the
component's root div with className "rounded-100 border-border-subtle
min-h-[280px] border p-200") — find the matching min-height token from the
design token token set (or use the existing project token naming convention,
e.g., min-h-XXX) and update the className to use that token instead of
min-h-[280px].
In `@src/app/`(service)/(my)/my-study-review/group/page.tsx:
- Line 3: The import in page.tsx uses a relative path for
CompletedStudyReviewPage; update the import to use the project path alias by
replacing the relative import with
"@/app/(service)/(my)/my-study-review/_components/completed-study-review-page"
so CompletedStudyReviewPage is imported via the `@/` alias (adjust the import
statement in page.tsx to reference that module).
In `@src/app/`(service)/(my)/my-study-review/mentor/page.tsx:
- Line 3: The import for CompletedStudyReviewPage currently uses a relative
path; update the import to use the project alias (`@/`) mapped to ./src/ to follow
the Component path aliases guideline. Replace the line importing
CompletedStudyReviewPage (import CompletedStudyReviewPage from
'../_components/completed-study-review-page') with an alias import that starts
with "@/..." pointing to the same module, ensuring the symbol
CompletedStudyReviewPage and the module name completed-study-review-page remain
unchanged so other references continue to work. Verify TypeScript resolves the
alias (tsconfig paths) and run a quick build/type-check to confirm no import
errors.
In
`@src/app/`(service)/(my)/my-study-review/one-to-one/_components/one-to-one-review-card.tsx:
- Line 7: Replace the relative import of useExpandableContent with the project
alias: update the import statement that currently references
'../../_components/use-expandable-content' in one-to-one-review-card.tsx to use
the alias module
'@/app/(service)/(my)/my-study-review/_components/use-expandable-content' and
keep the imported symbol name useExpandableContent unchanged so tooling and
refactors resolve correctly.
In `@src/components/common/modals/group-study-review-modal.tsx`:
- Line 56: Replace all forbidden arbitrary Tailwind utilities (max-w-[360px],
h-[120px], h-[52px], w-[52px], text-[28px]) with the project's design-token
classes; locate the usages in the JSX (e.g., the Modal.Title element and the
avatar/box elements that currently use those bracketed utilities) and swap each
arbitrary value for the equivalent token class (or add a new design token if
none exists), updating the className strings accordingly so only approved token
classes remain.
In `@src/components/common/modals/study-completion-modal.tsx`:
- Around line 53-60: The confetti color hexes and the hardcoded `text-[64px]` in
the StudyCompletionModal are forbidden; locate the confetti invocations (calls
to fire(...) with the colors: [...] arrays) and replace the hard-coded hex
values with the theme color tokens or CSS variables exposed in global.css (use
the same token names your project uses for primary/accent/grape/amber/green),
and replace `text-[64px]` with the appropriate project Tailwind/custom class (or
an `@theme` inline token class) that maps to the 64px size; ensure you read tokens
from the global theme (or use var(--your-token)) rather than literals so theme
changes apply consistently.
In `@src/components/common/ui/star-rating-input.tsx`:
- Around line 13-19: The special-case branch in roundToNearestHalf is
unnecessary; remove the if (num === 0) block and simplify the function to always
return Math.round(num * 2) / 2 so zero and other values are handled uniformly;
update the function roundToNearestHalf to a single return expression and ensure
callers still expect a number (no toFixed/Number conversion).
In `@src/components/forms/group-study-form.tsx`:
- Around line 24-25: Extract the ModeContext and useMode (and useClassification
if present) into a dedicated module (e.g., a new
src/contexts/group-study-mode.ts) so they are no longer defined inside
group-study-form.tsx; move the createContext<'create' | 'edit'>('create')
declaration and the export const useMode hook into that new file, export both
the context and hook, then update group-study-form.tsx and step1-group.tsx to
import ModeContext/useMode (and useClassification) from the new module; ensure
you remove the original declarations from group-study-form.tsx to break the
circular dependency and keep the same exported identifiers (ModeContext,
useMode, useClassification) so callers need minimal changes.
In `@src/hooks/queries/group-study-review-api.ts`:
- Around line 130-136: In the onSuccess callback where you currently await
queryClient.invalidateQueries twice in sequence (using
groupStudyReviewQueryKeys.lists() and
groupStudyReviewQueryKeys.written(variables.groupStudyId)), run both
invalidations in parallel with Promise.all to avoid serial waits; update the
onSuccess handler that calls queryClient.invalidateQueries with
groupStudyReviewQueryKeys.lists() and groupStudyReviewQueryKeys.written(...) to
invoke them inside Promise.all, and make the same change for the similar block
referenced around lines 154-161.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5fbacaae-0bc0-482c-b8b7-bafbb92d6b70
📒 Files selected for processing (28)
src/app/(service)/(my)/layout.tsxsrc/app/(service)/(my)/my-study-review/_components/completed-study-review-page.tsxsrc/app/(service)/(my)/my-study-review/_components/review-page-content.tsxsrc/app/(service)/(my)/my-study-review/_components/study-review-tab-nav.tsxsrc/app/(service)/(my)/my-study-review/_components/use-expandable-content.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/_hooks/use-review-statistics.tssrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/page.tsxsrc/app/(service)/(my)/my-study-review/group/_components/evaluation-section.tsxsrc/app/(service)/(my)/my-study-review/group/_components/group-review-card.tsxsrc/app/(service)/(my)/my-study-review/group/_components/member-study-card.tsxsrc/app/(service)/(my)/my-study-review/group/_components/satisfaction-section.tsxsrc/app/(service)/(my)/my-study-review/group/page.tsxsrc/app/(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsxsrc/app/(service)/(my)/my-study-review/mentor/page.tsxsrc/app/(service)/(my)/my-study-review/one-to-one/_components/one-to-one-review-card.tsxsrc/app/(service)/(my)/my-study-review/one-to-one/_utils.tssrc/app/(service)/(my)/my-study-review/one-to-one/page.tsxsrc/app/(service)/(my)/my-study-review/page.tsxsrc/components/common/modals/group-study-form-modal.tsxsrc/components/common/modals/group-study-review-modal.tsxsrc/components/common/modals/study-completion-modal.tsxsrc/components/common/modals/study-review-modal.tsxsrc/components/common/ui/star-rating-input.tsxsrc/components/forms/group-study-form.tsxsrc/components/forms/group-study-steps/step1-group.tsxsrc/hooks/common/use-group-study-review-form.tssrc/hooks/queries/group-study-review-api.tssrc/types/schemas/group-study-form.schema.ts
| <div className="flex-1 px-400 pt-400 pb-[100px] lg:px-0 lg:pt-500"> | ||
| <div className="w-full max-w-[780px] mx-auto">{children}</div> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check available spacing and width tokens in global.css
# Search for spacing tokens
rg -n "spacing|padding|--p-|--pb-" src/app/global.css
# Search for max-width tokens
rg -n "max-w|--max-w" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 3699
🏁 Script executed:
#!/bin/bash
# Check tailwind config to understand token naming and scale
fd -t f 'tailwind.config' . | head -5Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Look for any other pb (padding-bottom) token definitions
rg -n "pb-|padding-bottom" src/app/global.css | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Check if pb-400, pb-500 tokens exist
rg -n "@utility pb-" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Get the full context of lines 20-21 in the file
sed -n '15,25p' src/app/\(service\)/\(my\)/layout.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 364
🏁 Script executed:
#!/bin/bash
# Search more broadly for tailwind config files
find . -maxdepth 2 -name "tailwind.config.*" -o -name "tsconfig.json" | grep -E "(tailwind|ts)" | head -10Repository: code-zero-to-one/study-platform-client
Length of output: 97
🏁 Script executed:
#!/bin/bash
# Check if there's a CSS or config that might define pb-400, pb-500, etc. as utilities
rg -n "@utility.*p" src/app/global.css | grep -i "pb\|padding" | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Look at the complete spacing/utility definition section in global.css
sed -n '374,700p' src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 6007
🏁 Script executed:
#!/bin/bash
# Check if there's a pb-400 or pb-500 utility defined elsewhere
rg -n "@utility pb-" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Look for any dynamic utility pattern or theme extension
grep -n "pb-\|padding-bottom" src/app/global.css | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# Check if there are any other max-w definitions that might match 780px
rg -n "max-w-|@utility.*w" src/app/global.css | grep "max-w"Repository: code-zero-to-one/study-platform-client
Length of output: 167
🏁 Script executed:
#!/bin/bash
# Verify if this is an admin page (exempt from custom token rules)
head -5 src/app/\(service\)/\(my\)/layout.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 317
Tailwind 임의 값 사용이 코딩 가이드라인을 위반합니다.
pb-[100px]와 max-w-[780px]는 임의 값(arbitrary values)을 사용하고 있습니다. 프로젝트 커스텀 디자인 토큰만 사용해야 합니다. 예를 들어, px-400과 pt-400, pt-500은 올바르게 global.css에 정의된 spacing 토큰을 사용하고 있습니다. 비슷하게 pb-와 max-w-도 정의된 토큰 또는 새로운 utility 클래스를 사용해 주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(service)/(my)/layout.tsx around lines 20 - 21, The div in
layout.tsx is using Tailwind arbitrary values pb-[100px] and max-w-[780px],
which violates the design-token rule; replace pb-[100px] with an existing
spacing token class (e.g., pb-400) or add a new spacing token in global.css and
use that token's class, and replace max-w-[780px] with a custom max-width
utility defined in your design tokens (e.g., add a max-w-<token> in global.css
and use that class) so both pb and max-w use project-approved tokens instead of
arbitrary values.
| const [expanded, setExpanded] = useState(false); | ||
| const visible = expanded ? items : items.slice(0, DEFAULT_SHOW_COUNT); | ||
| const hasMore = items.length > DEFAULT_SHOW_COUNT; |
There was a problem hiding this comment.
showToggle이 없는 카드가 6번째 항목부터 영구히 잘립니다.
visible이 항상 앞 5개만 보여주도록 계산돼서, 지금처럼 두 번째 카드에 showToggle을 넘기지 않으면 disappointedItems가 5개를 넘어도 나머지 항목을 볼 방법이 없습니다.
수정 예시
- const visible = expanded ? items : items.slice(0, DEFAULT_SHOW_COUNT);
+ const visible =
+ !showToggle || expanded ? items : items.slice(0, DEFAULT_SHOW_COUNT);Also applies to: 114-117
| const { data: positiveData } = useUserPositiveKeywordsQuery({}); | ||
| const { data: negativeData } = useMyNegativeKeywordsQuery({}); | ||
| const { | ||
| data: reviewData, | ||
| fetchNextPage, | ||
| hasNextPage, | ||
| } = useMyReviewsInfinityQuery(); | ||
|
|
||
| const statistics = buildEvaluationStatistics( | ||
| positiveData?.keywords ?? [], | ||
| negativeData?.keywords ?? [], | ||
| ); | ||
|
|
||
| const reviews = reviewData?.reviews ?? []; | ||
| const totalCount = reviewData?.totalCount ?? 0; | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-400"> | ||
| <StudyReviewTabNav /> | ||
|
|
||
| <EvaluationSection statistics={statistics} /> |
There was a problem hiding this comment.
로딩/실패 상태가 실제 빈 후기 상태로 보입니다.
지금은 세 쿼리 중 하나라도 아직 로딩 중이거나 실패하면 []/0 폴백 때문에 바로 후기 0건과 빈 상태 문구가 노출됩니다. 사용자는 데이터가 없는 건지, 요청이 실패한 건지 구분할 수 없습니다. 로딩/에러 상태를 먼저 분리해서 인라인으로 보여주는 쪽이 안전합니다.
As per coding guidelines For recoverable failures in UI: use inline error display first, Toast as secondary.
Also applies to: 49-58
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/app/`(service)/(my)/my-study-review/one-to-one/page.tsx around lines 16 -
36, The page currently falls back to empty arrays/0 for positiveData,
negativeData and reviewData which makes loading/errors look like "0 reviews";
update the component to surface loading and error states from
useUserPositiveKeywordsQuery, useMyNegativeKeywordsQuery and
useMyReviewsInfinityQuery before computing statistics or rendering
EvaluationSection and the reviews empty state: check each query's
isLoading/isFetching and isError/error and render inline loaders or error
messages (per guidelines: inline error first, optional Toast secondary) instead
of proceeding to buildEvaluationStatistics or using reviews/totalCount
fallbacks; only compute statistics and render EvaluationSection/reviews list
when the relevant queries have succeeded.
| <GroupStudyForm | ||
| methods={createMethods} | ||
| onSubmit={handleSubmitForm} | ||
| mode="create" | ||
| /> | ||
| )} | ||
| {mode === 'edit' && isGroupStudyLoading && ( | ||
| <Modal.Body className="font-designer-16m text-text-subtle py-800 text-center"> | ||
| <Modal.Body className="font-designer-16m text-text-subtle py-700 text-center"> | ||
| 스터디 정보를 불러오는 중입니다... | ||
| </Modal.Body> | ||
| )} | ||
| {mode === 'edit' && !isGroupStudyLoading && editDefaultValues && ( | ||
| <GroupStudyForm | ||
| methods={editMethods} | ||
| onSubmit={handleSubmitForm} | ||
| mode="edit" |
There was a problem hiding this comment.
mode prop만 넘겨서는 수정 모드 시작일 버그가 아직 남습니다.
이 파일에서 mode="edit"를 내려줘도, 실제 폼 단계가 그 값을 소비하지 않으면 달력 min은 그대로 "내일"에 묶입니다. 그러면 스키마는 허용해도 수정 모달 UI에서는 오늘/기존 과거 시작일을 여전히 선택할 수 없습니다.
Based on learnings step1-group.tsx always sets the date input min prop to tomorrow regardless of create/edit mode, and GroupStudyForm does not pass a mode or minDate prop to Step1OpenGroupStudy.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/common/modals/group-study-form-modal.tsx` around lines 320 -
335, The edit modal still forces the start-date min to "tomorrow" because
GroupStudyForm doesn't forward the edit mode or a minDate to the
Step1OpenGroupStudy component; update GroupStudyForm (the component rendered
with methods={createMethods} and methods={editMethods}) to accept and forward a
mode prop (or compute/passthrough a minDate) into Step1OpenGroupStudy, and
update step1-group.tsx (Step1OpenGroupStudy) to use the passed mode/minDate (or
the existing default startDate when mode === 'edit') when computing the date
input's min instead of always using tomorrow so the edit flow can select
today/past existing start dates.
| const startDateMin = | ||
| mode === 'create' | ||
| ? formatKoreaYMD(addDays(getKoreaDate(), 1)) | ||
| : formatKoreaYMD(getKoreaDate()); |
There was a problem hiding this comment.
편집 모드에서 과거 시작일은 아직 선택 불가입니다.
mode !== 'create'일 때 min을 오늘로 고정하면, 이미 시작된 스터디를 수정할 때 기존 startDate가 오늘보다 과거인 경우 브라우저 date input이 invalid 상태가 됩니다. 편집 모드에서는 현재 값이 과거면 그 값을 min으로 써서 “기존 값 유지” 시나리오를 열어 둬야 합니다.
🗓️ 제안 수정
+ const today = formatKoreaYMD(getKoreaDate());
+ const currentStartDate = watch('startDate');
const startDateMin =
mode === 'create'
- ? formatKoreaYMD(addDays(getKoreaDate(), 1))
- : formatKoreaYMD(getKoreaDate());
+ ? formatKoreaYMD(addDays(getKoreaDate(), 1))
+ : currentStartDate && currentStartDate < today
+ ? currentStartDate
+ : today;Also applies to: 248-248
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/forms/group-study-steps/step1-group.tsx` around lines 69 - 72,
The current startDateMin computation forces min to today/tomorrow even in edit
mode, which invalidates an existing past startDate; update the logic that
computes startDateMin (the variable using mode, formatKoreaYMD, addDays,
getKoreaDate) so that in 'create' mode it remains tomorrow, but in edit mode it
uses the earlier of the current date and the preloaded startDate (i.e., if the
form's existing startDate is before today, use that existing value as min) —
locate where startDateMin is calculated and replace the fixed-today branch with
a comparison between getKoreaDate() (or addDays for create) and the form's
current startDate value so the existing past date remains selectable.
| const handleSubmit = () => { | ||
| setHasAttemptedSubmit(true); | ||
| if (!isFormValid || !form.satisfaction) return; | ||
|
|
||
| const { satisfaction, selectedCodes, content, rating } = form; | ||
|
|
||
| createReview( | ||
| { | ||
| groupStudyId, | ||
| request: { | ||
| satisfaction, | ||
| selectableReviewItemCodes: selectedCodes, | ||
| content, | ||
| rating: Math.round(rating), // 반별점(0.5 단위) → 백엔드 정수 변환 | ||
| }, | ||
| }, | ||
| { | ||
| onSuccess: () => { | ||
| showToast('후기가 작성되었습니다.', 'success'); | ||
| onClose(); | ||
| onSubmitSuccess?.(); | ||
| }, | ||
| }, | ||
| ); | ||
| }; |
There was a problem hiding this comment.
에러 처리가 누락되어 실패 시 사용자에게 피드백이 없습니다.
createReview 뮤테이션에 onSuccess만 정의되어 있고 onError가 없습니다. 코딩 가이드라인에 따르면 뮤테이션 콜백에서 onError로 에러 토스트를 표시해야 합니다. 후기 작성 실패 시 사용자가 문제를 인지하지 못할 수 있습니다.
🐛 에러 처리 추가 제안
+import { analyzeError } from '@/utils/error-handler';
createReview(
{
groupStudyId,
request: {
satisfaction,
selectableReviewItemCodes: selectedCodes,
content,
rating: Math.round(rating),
},
},
{
onSuccess: () => {
showToast('후기가 작성되었습니다.', 'success');
onClose();
onSubmitSuccess?.();
},
+ onError: (error) => {
+ const errorInfo = analyzeError(error);
+ showToast(errorInfo.userMessage, 'error');
+ },
},
);As per coding guidelines: "For useMutation hooks, use onSuccess for success-only actions, onError for error handling."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/hooks/common/use-group-study-review-form.ts` around lines 116 - 140, The
handleSubmit function calls createReview with only onSuccess; add an onError
handler to the createReview call so failures surface to the user: inside the
createReview options object, implement onError to call showToast with an error
message (and optionally log the error) and ensure any UI state cleanup (e.g.,
closing modal or leaving form state) is handled appropriately; update the
createReview invocation in use-group-study-review-form.ts (the createReview call
inside handleSubmit) to include this onError callback.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (6)
src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx (1)
30-30: 코딩 가이드라인 위반: 임의 값 사용
min-h-[280px]는 Tailwind 임의 값입니다. 프로젝트 가이드라인에 따라 커스텀 디자인 토큰만 사용해야 합니다.♻️ 수정 제안
프로젝트의 spacing 토큰을 확인하여 적절한 값으로 대체하거나, 필요시
global.css에 새로운 토큰을 추가하세요.- <div className="rounded-100 border-border-subtle min-h-[280px] border p-200"> + <div className="rounded-100 border-border-subtle min-h-3500 border p-200">(실제 토큰 값은 프로젝트의 디자인 시스템에 맞게 조정 필요)
As per coding guidelines:
Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx at line 30, The div in EvaluationSection component uses a Tailwind arbitrary value `min-h-[280px]`, violating the project's design-token-only rule; replace that arbitrary class with the appropriate project spacing token class (e.g., the component's existing spacing token such as a min-h token from your design system) on the element in evaluation-section.tsx (the div with className including rounded-100 border-border-subtle min-h-[280px] border p-200), and if no suitable token exists add a new min-height token in global.css (naming consistent with other tokens) then update the className to use that token.src/app/(service)/(my)/my-study-review/group/[groupStudyId]/page.tsx (1)
12-15: Import 순서 정리 필요린터 경고에 따르면
GroupReviewCardimport가SatisfactionSectionimport보다 앞에 와야 합니다.♻️ 수정 제안
import EvaluationSection from './_components/evaluation-section'; +import GroupReviewCard from './_components/group-review-card'; import SatisfactionSection from './_components/satisfaction-section'; import { useReviewStatistics } from './_hooks/use-review-statistics'; -import GroupReviewCard from './_components/group-review-card';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/page.tsx around lines 12 - 15, Import order is incorrect: move the GroupReviewCard import so it appears before SatisfactionSection to satisfy the linter. Open the imports at the top of page.tsx and reorder the lines so GroupReviewCard is imported prior to SatisfactionSection (keeping EvaluationSection and useReviewStatistics positions as needed), then save and re-run the linter/formatter to confirm the warning is resolved.src/app/(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsx (1)
13-17: 상대 경로 대신@/alias 사용 권장코딩 가이드라인에 따라 모든 import에
@/prefix를 사용해야 합니다. 또한 린터 경고에 따르면 import 순서도 정리가 필요합니다.♻️ 수정 제안
-import { useReviewStatistics } from '../../group/[groupStudyId]/_hooks/use-review-statistics'; - -import EvaluationSection from '../../group/[groupStudyId]/_components/evaluation-section'; -import SatisfactionSection from '../../group/[groupStudyId]/_components/satisfaction-section'; -import GroupReviewCard from '../../group/[groupStudyId]/_components/group-review-card'; +import EvaluationSection from '@/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section'; +import GroupReviewCard from '@/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/group-review-card'; +import SatisfactionSection from '@/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/satisfaction-section'; +import { useReviewStatistics } from '@/app/(service)/(my)/my-study-review/group/[groupStudyId]/_hooks/use-review-statistics';As per coding guidelines:
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsx around lines 13 - 17, The imports in page.tsx use relative paths instead of the project alias and violate import ordering rules; update the four imports (useReviewStatistics, EvaluationSection, SatisfactionSection, GroupReviewCard) to use the "@/..." alias that maps to ./src/, and reorder them to satisfy the linter (group external modules, then "@/..." imports, alphabetize or follow your repo's import-order rules) so the linter warnings go away.src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/satisfaction-section.tsx (1)
71-71: 코딩 가이드라인 위반: 임의 값 사용
sm:w-[88px]와sm:w-[96px](lines 71, 83, 89, 101)는 Tailwind 임의 값입니다. 프로젝트 디자인 토큰을 사용해야 합니다.♻️ 수정 제안
프로젝트의 width 토큰으로 대체하거나, 반복적으로 사용되는 값이라면
global.css에 커스텀 토큰을 추가하세요.- <div className="flex items-center gap-75 sm:w-[88px] sm:shrink-0"> + <div className="flex items-center gap-75 sm:w-1100 sm:shrink-0"> ... - <span className="font-designer-14m text-text-subtle shrink-0 text-left tabular-nums sm:w-[96px] sm:text-right"> + <span className="font-designer-14m text-text-subtle shrink-0 text-left tabular-nums sm:w-1200 sm:text-right">(실제 토큰 값은 프로젝트 디자인 시스템 확인 필요)
As per coding guidelines:
Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/satisfaction-section.tsx at line 71, Replace the Tailwind arbitrary width values in the satisfaction-section component with your project's design tokens: locate the div that contains the class string "flex items-center gap-75 sm:w-[88px] sm:shrink-0" (and the other occurrences using sm:w-[96px]) in src/app/.../satisfaction-section.tsx, remove the arbitrary sm:w-[88px]/sm:w-[96px] values and swap them for the appropriate project width token class (e.g., sm:w-{your-token}) or a named utility; if the required width token does not exist, add a CSS variable/utility in global.css (e.g., --width-<name> and a .w-<name> helper) and then use that token/helper in the component classNames instead of the arbitrary bracket syntax.src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/group-review-card.tsx (1)
7-7:@/경로 alias 사용 권장코딩 가이드라인에 따라 모든 import에
@/prefix를 사용해야 합니다.♻️ 수정 제안
-import { useExpandableContent } from '../../../_components/use-expandable-content'; +import { useExpandableContent } from '@/app/(service)/(my)/my-study-review/_components/use-expandable-content';As per coding guidelines:
Component path aliases: use@/prefix mapped to ./src/ (configured in tsconfig.json) for all imports.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/group-review-card.tsx at line 7, Replace the relative import for useExpandableContent in group-review-card.tsx with the project path-alias form: locate the import of useExpandableContent in the file (symbol: useExpandableContent) and change the module specifier to use the '@/...' alias that maps to ./src/ per tsconfig (e.g., replace '../../../_components/use-expandable-content' with the corresponding '@/...' path to the same module), ensuring the import uses the alias style required by the coding guidelines.src/components/common/modals/group-study-review-modal.tsx (1)
115-116:Object.keys(...) as ReviewSatisfaction[]단언은 가드 패턴으로 바꿔 주세요.Line 115의 강제 단언은 런타임 안전성을 잃습니다. 백엔드 enum-like 문자열 처리 규칙에 맞게
in가드(또는 안전한 키 배열 상수)로 순회하는 방식이 더 안전합니다.As per coding guidelines
For enum-like string types from backend, use in operator guard with fallback instead of simple as type assertion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/modals/group-study-review-modal.tsx` around lines 115 - 116, Replace the unsafe type assertion "(Object.keys(SATISFACTION_CONFIG) as ReviewSatisfaction[])" with a runtime-safe guard: iterate over a validated keys array or filter Object.keys(...) using an "in" check against SATISFACTION_CONFIG (or use a constant array of allowed ReviewSatisfaction keys), then cast only after the guard; update the map call (the expression iterating SATISFACTION_CONFIG) to use that guarded keys list so each "key" is guaranteed to be a valid ReviewSatisfaction at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/common/modals/group-study-review-modal.tsx`:
- Line 58: The span currently renders `basicInfo?.startDate` and
`basicInfo?.endDate` directly, which can show "undefined ~ undefined"; update
the rendering in group-study-review-modal.tsx to provide fallbacks when those
fields are missing (e.g., use nullish coalescing or conditional rendering for
`basicInfo?.startDate` and `basicInfo?.endDate` and replace undefined with a
user-friendly placeholder like '-' or '미정'), ensuring the displayed string never
contains "undefined".
- Around line 29-32: Extract the inline object shapes into named interfaces and
use them instead of anonymous types: for SATISFACTION_CONFIG create an interface
(e.g., SatisfactionConfigItem { label: string; emoji: string }) and change the
type from Record<ReviewSatisfaction, { label: string; emoji: string }> to
Record<ReviewSatisfaction, SatisfactionConfigItem>; do the same for the other
inline object shapes found around the other mentioned blocks (lines 80-85 and
227-232) by declaring appropriately named interfaces and replacing the inline
object literal/type annotations with those interface names to follow the repo
coding guideline.
- Line 55: Replace all Tailwind arbitrary value classes in this component with
the project's custom design tokens: change Modal.Title's class from
max-w-[360px] to max-w-360; replace the element using h-[120px] with the closest
permitted height token (e.g., h-200) or discuss the correct token if none
exists; and update the small avatar/icon block where h-[52px], w-[52px],
text-[28px] are used to h-650, w-650 and a font token like font-designer-28r/m/b
respectively (remove the color `text-` prefix when replacing the font size).
Locate these occurrences in GroupStudyReviewModal (look for Modal.Title and the
elements with h-[120px], h-[52px], w-[52px], text-[28px]) and swap each
arbitrary class for the corresponding custom utility token.
---
Nitpick comments:
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx:
- Line 30: The div in EvaluationSection component uses a Tailwind arbitrary
value `min-h-[280px]`, violating the project's design-token-only rule; replace
that arbitrary class with the appropriate project spacing token class (e.g., the
component's existing spacing token such as a min-h token from your design
system) on the element in evaluation-section.tsx (the div with className
including rounded-100 border-border-subtle min-h-[280px] border p-200), and if
no suitable token exists add a new min-height token in global.css (naming
consistent with other tokens) then update the className to use that token.
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/group-review-card.tsx:
- Line 7: Replace the relative import for useExpandableContent in
group-review-card.tsx with the project path-alias form: locate the import of
useExpandableContent in the file (symbol: useExpandableContent) and change the
module specifier to use the '@/...' alias that maps to ./src/ per tsconfig
(e.g., replace '../../../_components/use-expandable-content' with the
corresponding '@/...' path to the same module), ensuring the import uses the
alias style required by the coding guidelines.
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/satisfaction-section.tsx:
- Line 71: Replace the Tailwind arbitrary width values in the
satisfaction-section component with your project's design tokens: locate the div
that contains the class string "flex items-center gap-75 sm:w-[88px]
sm:shrink-0" (and the other occurrences using sm:w-[96px]) in
src/app/.../satisfaction-section.tsx, remove the arbitrary
sm:w-[88px]/sm:w-[96px] values and swap them for the appropriate project width
token class (e.g., sm:w-{your-token}) or a named utility; if the required width
token does not exist, add a CSS variable/utility in global.css (e.g.,
--width-<name> and a .w-<name> helper) and then use that token/helper in the
component classNames instead of the arbitrary bracket syntax.
In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/page.tsx:
- Around line 12-15: Import order is incorrect: move the GroupReviewCard import
so it appears before SatisfactionSection to satisfy the linter. Open the imports
at the top of page.tsx and reorder the lines so GroupReviewCard is imported
prior to SatisfactionSection (keeping EvaluationSection and useReviewStatistics
positions as needed), then save and re-run the linter/formatter to confirm the
warning is resolved.
In `@src/app/`(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsx:
- Around line 13-17: The imports in page.tsx use relative paths instead of the
project alias and violate import ordering rules; update the four imports
(useReviewStatistics, EvaluationSection, SatisfactionSection, GroupReviewCard)
to use the "@/..." alias that maps to ./src/, and reorder them to satisfy the
linter (group external modules, then "@/..." imports, alphabetize or follow your
repo's import-order rules) so the linter warnings go away.
In `@src/components/common/modals/group-study-review-modal.tsx`:
- Around line 115-116: Replace the unsafe type assertion
"(Object.keys(SATISFACTION_CONFIG) as ReviewSatisfaction[])" with a runtime-safe
guard: iterate over a validated keys array or filter Object.keys(...) using an
"in" check against SATISFACTION_CONFIG (or use a constant array of allowed
ReviewSatisfaction keys), then cast only after the guard; update the map call
(the expression iterating SATISFACTION_CONFIG) to use that guarded keys list so
each "key" is guaranteed to be a valid ReviewSatisfaction at runtime.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8e653a45-475d-41e7-b42f-47e6800b5834
📒 Files selected for processing (8)
src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/group-review-card.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/satisfaction-section.tsxsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/page.tsxsrc/app/(service)/(my)/my-study-review/mentor/[groupStudyId]/page.tsxsrc/app/(service)/(my)/my-study-review/one-to-one/page.tsxsrc/components/common/modals/group-study-review-modal.tsxsrc/types/api/group-study-review.types.ts
✅ Files skipped from review due to trivial changes (2)
- src/types/api/group-study-review.types.ts
- src/app/(service)/(my)/my-study-review/one-to-one/page.tsx
| const SATISFACTION_CONFIG: Record< | ||
| ReviewSatisfaction, | ||
| { label: string; emoji: string } | ||
| > = { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, verify the file exists and get its structure
wc -l src/components/common/modals/group-study-review-modal.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 143
🏁 Script executed:
# Read the specific lines mentioned in the review comment
sed -n '29,32p' src/components/common/modals/group-study-review-modal.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 179
🏁 Script executed:
# Read lines 80-85
sed -n '80,85p' src/components/common/modals/group-study-review-modal.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 187
🏁 Script executed:
# Read lines 227-232
sed -n '227,232p' src/components/common/modals/group-study-review-modal.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 171
객체 shape 정의 시 interface 선언으로 분리해 주세요.
Lines 29-32, 80-85, 227-232에서 객체 구조를 인라인으로 정의하고 있습니다. 이 레포의 코딩 가이드라인에 따르면 객체 구조는 interface로 선언되어야 합니다.
♻️ 제안 코드
+interface SatisfactionConfigItem {
+ label: string;
+ emoji: string;
+}
+
+interface GroupStudyReviewFormProps {
+ open: boolean;
+ groupStudyId: number;
+ onClose: () => void;
+ onSubmitSuccess?: () => void;
+}
+
+interface SatisfactionButtonProps {
+ label: string;
+ isSelected: boolean;
+ onClick: () => void;
+ emoji: string;
+}
+
-const SATISFACTION_CONFIG: Record<
- ReviewSatisfaction,
- { label: string; emoji: string }
-> = {
+const SATISFACTION_CONFIG: Record<ReviewSatisfaction, SatisfactionConfigItem> = {
GOOD: { label: '좋았어요', emoji: '\u{1F60A}' },
DISAPPOINTED: { label: '아쉬웠어요', emoji: '\u{1F605}' },
};
-function GroupStudyReviewForm({
+function GroupStudyReviewForm({
open,
groupStudyId,
onClose,
onSubmitSuccess,
-}: {
- open: boolean;
- groupStudyId: number;
- onClose: () => void;
- onSubmitSuccess?: () => void;
-}) {
+}: GroupStudyReviewFormProps) {
-function SatisfactionButton({
+function SatisfactionButton({
label,
isSelected,
onClick,
emoji,
-}: {
- label: string;
- isSelected: boolean;
- onClick: () => void;
- emoji: string;
-}) {
+}: SatisfactionButtonProps) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/common/modals/group-study-review-modal.tsx` around lines 29 -
32, Extract the inline object shapes into named interfaces and use them instead
of anonymous types: for SATISFACTION_CONFIG create an interface (e.g.,
SatisfactionConfigItem { label: string; emoji: string }) and change the type
from Record<ReviewSatisfaction, { label: string; emoji: string }> to
Record<ReviewSatisfaction, SatisfactionConfigItem>; do the same for the other
inline object shapes found around the other mentioned blocks (lines 80-85 and
227-232) by declaring appropriately named interfaces and replacing the inline
object literal/type annotations with those interface names to follow the repo
coding guideline.
| > | ||
| <Modal.Header variant="form"> | ||
| <div className="flex flex-col gap-50"> | ||
| <Modal.Title className="font-designer-16b text-text-default truncate max-w-[360px]"> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n src/components/common/modals/group-study-review-modal.tsx | head -60 | tail -10Repository: code-zero-to-one/study-platform-client
Length of output: 633
🏁 Script executed:
cat -n src/components/common/modals/group-study-review-modal.tsx | sed -n '50,60p'Repository: code-zero-to-one/study-platform-client
Length of output: 696
🏁 Script executed:
cat -n src/components/common/modals/group-study-review-modal.tsx | sed -n '170,180p'Repository: code-zero-to-one/study-platform-client
Length of output: 526
🏁 Script executed:
cat -n src/components/common/modals/group-study-review-modal.tsx | sed -n '240,250p'Repository: code-zero-to-one/study-platform-client
Length of output: 505
🏁 Script executed:
# Check if global.css exists and what tokens are available
head -100 src/app/global.css | grep -E "(--.*:|@theme)"Repository: code-zero-to-one/study-platform-client
Length of output: 853
🏁 Script executed:
# Check if there are sizing/spacing token definitions in global.css
rg -A 2 "(--spacing|--height|--width|--size|--radius)" src/app/global.css | head -40Repository: code-zero-to-one/study-platform-client
Length of output: 844
🏁 Script executed:
# Check all custom token definitions in global.css
cat src/app/global.css | grep "^[[:space:]]*--" | head -50Repository: code-zero-to-one/study-platform-client
Length of output: 1496
🏁 Script executed:
# Check if there are Tailwind custom classes defined (e.g., w-400, h-100, etc.)
rg -E "^\s+\.[wh]-\[" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 132
🏁 Script executed:
# Look for sizing scale in global.css more broadly
sed -n '/@theme/,/^}/p' src/app/global.css | head -100Repository: code-zero-to-one/study-platform-client
Length of output: 2654
🏁 Script executed:
# Check if there are custom Tailwind utility classes defined in tailwind.config or elsewhere
find src -name "tailwind.config.*" -o -name "*.css" | xargs grep -l "extend\|@layer utilities" 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 100
🏁 Script executed:
# Check tailwind.config for extend section
find . -maxdepth 3 -name "tailwind.config.*" -type f -exec cat {} \;Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Look for any size/dimension token definitions beyond spacing
grep -E "(--size|--width|--height|--font-size)" src/app/global.css | head -30Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Check tailwind.config.ts or .js for extend section with width/height/fontSize
cat tailwind.config.ts 2>/dev/null || cat tailwind.config.js 2>/dev/null || echo "No tailwind config found"Repository: code-zero-to-one/study-platform-client
Length of output: 106
🏁 Script executed:
# Search for custom class definitions that match w-, h-, text- patterns
rg "w-\d+|h-\d+|text-\d+" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 1669
🏁 Script executed:
# Check all `@layer` definitions in global.css
grep -A 5 "@layer" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 685
🏁 Script executed:
# Check if spacing tokens are used for width/height via Tailwind mapping
grep -E "(width|height|fontSize)" tailwind.config.ts tailwind.config.js 2>/dev/null || rg "extend.*:" src -t ts | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 335
🏁 Script executed:
# Get the full definitions of the custom utilities to see their actual values
grep -A 1 "@utility" src/app/global.css | head -40Repository: code-zero-to-one/study-platform-client
Length of output: 629
🏁 Script executed:
# Check what w-360, h-200, w-320, h-650 etc. map to in pixels
grep -B 1 -A 3 "@utility w-360\|@utility w-320\|@utility h-200\|@utility h-650\|@utility h-500px" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 270
🏁 Script executed:
# Check for font-size utilities and h-120
grep -A 1 "@utility.*text-\|@utility.*font-\|@utility.*h-1" src/app/global.css | grep -E "(`@utility`|font-size|height)" | head -30Repository: code-zero-to-one/study-platform-client
Length of output: 801
🏁 Script executed:
# Get full list of all custom utilities defined
grep "@utility" src/app/global.css | sed 's/@utility //g' | sed 's/ {//'Repository: code-zero-to-one/study-platform-client
Length of output: 1264
🏁 Script executed:
# Check for max-width utilities and h-120 or similar
grep -E "@utility max-w|@utility h-1" src/app/global.css | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 155
🏁 Script executed:
# Check the exact pixels for all height utilities starting with h-
grep -B 1 "height:" src/app/global.css | grep -E "@utility h-|height:"Repository: code-zero-to-one/study-platform-client
Length of output: 2773
🏁 Script executed:
# Check max-w utilities with their pixel values
grep -A 1 "@utility max-w-" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 223
🏁 Script executed:
# Check for h-120 and other h-1xx utilities
rg "@utility h-1[0-9]{2}" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Check spacing scale - maybe 120 can be built from spacing
grep -E "spacing-.*120|spacing-.*130" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Check if text-28 or similar font utilities exist
grep -E "@utility text-|font-size.*28" src/app/global.css | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 241
🏁 Script executed:
# Find the exact definition of h-650 and w-650 to understand naming
grep -A 1 "@utility h-650\|@utility w-650" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 149
🏁 Script executed:
# Check if there are other h-* utilities with pixel values around 120
grep -E "@utility h-[0-9]+" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 134
🏁 Script executed:
# Check spacing tokens - maybe h-120 should be built from spacing scale
grep "spacing-.*:.*120px" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
# Verify the font-designer-28 utilities exist
grep "font-designer-28" src/app/global.cssRepository: code-zero-to-one/study-platform-client
Length of output: 237
Tailwind 임의값을 프로젝트 커스텀 토큰으로 교체해 주세요.
아래 임의값들이 현재 프로젝트 규칙에서 금지되어 있습니다. 커스텀 유틸리티 클래스로 교체하세요:
- Line 55:
max-w-[360px]→max-w-360 - Line 173:
h-[120px]→ 대응하는 커스텀 토큰이 없으므로 가장 가까운 높이 유틸리티(h-200또는 다른 옵션) 검토 필요 - Line 245:
h-[52px]→h-650w-[52px]→w-650text-[28px]→font-designer-28r(또는28m,28b),text-프리픽스는 색상용이므로 수정 필요
코딩 가이드라인
Forbidden: Tailwind arbitrary values (e.g., `p-[4px]`, `w-[320px]`). Use only project custom design tokens.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/common/modals/group-study-review-modal.tsx` at line 55,
Replace all Tailwind arbitrary value classes in this component with the
project's custom design tokens: change Modal.Title's class from max-w-[360px] to
max-w-360; replace the element using h-[120px] with the closest permitted height
token (e.g., h-200) or discuss the correct token if none exists; and update the
small avatar/icon block where h-[52px], w-[52px], text-[28px] are used to h-650,
w-650 and a font token like font-designer-28r/m/b respectively (remove the color
`text-` prefix when replacing the font size). Locate these occurrences in
GroupStudyReviewModal (look for Modal.Title and the elements with h-[120px],
h-[52px], w-[52px], text-[28px]) and swap each arbitrary class for the
corresponding custom utility token.
| <Modal.Title className="font-designer-16b text-text-default truncate max-w-[360px]"> | ||
| {detailInfo.title} | ||
| </Modal.Title> | ||
| <span className="font-designer-13r text-text-subtlest">{`${basicInfo?.startDate} ~ ${basicInfo?.endDate}`}</span> |
There was a problem hiding this comment.
날짜 필드가 비어 있으면 undefined ~ undefined가 노출될 수 있습니다.
Line 58은 optional 체이닝 결과를 그대로 문자열로 렌더링하므로, 값이 없을 때 사용자에게 undefined가 보일 수 있습니다. 기본 문구 fallback을 넣는 편이 안전합니다.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/common/modals/group-study-review-modal.tsx` at line 58, The
span currently renders `basicInfo?.startDate` and `basicInfo?.endDate` directly,
which can show "undefined ~ undefined"; update the rendering in
group-study-review-modal.tsx to provide fallbacks when those fields are missing
(e.g., use nullish coalescing or conditional rendering for
`basicInfo?.startDate` and `basicInfo?.endDate` and replace undefined with a
user-friendly placeholder like '-' or '미정'), ensuring the displayed string never
contains "undefined".
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (1)
src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx (1)
26-27:⚠️ Potential issue | 🟠 Major
showToggle=false일 때 항목이 영구히 잘립니다.Line 26에서
visible계산이showToggle을 고려하지 않아, 토글 버튼이 없는 카드도 5개까지만 보입니다.수정 예시
- const visible = expanded ? items : items.slice(0, DEFAULT_SHOW_COUNT); + const visible = + !showToggle || expanded ? items : items.slice(0, DEFAULT_SHOW_COUNT);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx around lines 26 - 27, The visible calculation wrongly ignores showToggle: update the logic that sets visible (currently using expanded, items, DEFAULT_SHOW_COUNT) to respect showToggle so that when showToggle is false all items are shown; modify the expression that computes visible (and adjust hasMore if needed) to branch on showToggle (e.g., if !showToggle then visible = items and hasMore = false, otherwise preserve the existing expanded / slice behavior).
🧹 Nitpick comments (2)
src/hooks/common/use-group-study-review-reminder.ts (2)
73-78: useEffect 의존성 배열에 객체 참조 사용
pendingStudy는 매 렌더링마다 새로 생성되는 객체이므로 참조가 변경됩니다.hasAutoOpenedRef로 보호되어 있어 현재 동작에는 문제가 없지만,pendingStudy?.studyId처럼 primitive 값을 의존성으로 사용하면 불필요한 effect 실행을 방지할 수 있습니다.🔧 의존성 개선 제안
useEffect(() => { if (pendingStudy && allLoaded && !hasAutoOpenedRef.current) { hasAutoOpenedRef.current = true; setShowReviewModal(true); } - }, [pendingStudy, allLoaded]); + }, [pendingStudy?.studyId, allLoaded]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/common/use-group-study-review-reminder.ts` around lines 73 - 78, useEffect currently depends on the object pendingStudy which is recreated each render; change the dependency to a stable primitive (e.g., pendingStudy?.studyId) so the effect only runs when the actual study changes: update the dependency array in the useEffect that checks pendingStudy, allLoaded, hasAutoOpenedRef and calls setShowReviewModal to use pendingStudy?.studyId (and allLoaded) instead of pendingStudy to avoid unnecessary effect runs while keeping the hasAutoOpenedRef protection.
80-89: 타입 단언(type assertion) 대신 명시적 객체 생성 권장
as GroupStudyDetailInfoResponseDto/as GroupStudyBasicInfoResponseDto단언은pendingStudy에 DTO에서 요구하는 필드가 모두 있다고 가정하지만, 실제로는title,startTime,endTime만 사용하고 있습니다. 이 방식은 타입 안전성을 떨어뜨리고, DTO 타입이 변경될 때 런타임 문제가 발생할 수 있습니다.모달 컴포넌트가 실제로 필요로 하는 필드만 포함하는 별도의 인터페이스를 정의하거나,
Partial<T>또는Pick<T, K>을 사용하는 것을 권장합니다.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/common/use-group-study-review-reminder.ts` around lines 80 - 89, The current use of type assertions on reviewDetailInfo and reviewBasicInfo (casting to GroupStudyDetailInfoResponseDto / GroupStudyBasicInfoResponseDto) is unsafe; instead create explicit, narrower shapes using Pick or a local interface and construct objects with only the fields you actually use from pendingStudy (e.g., map title, startDate, endDate), or type them as Partial/ Pick<...> to reflect the subset; update the code that defines reviewDetailInfo and reviewBasicInfo to build plain objects from pendingStudy without "as" assertions and reference the new narrower type (or Partial/ Pick) so TypeScript enforces the correct fields at compile time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx:
- Around line 59-61: The numeric span currently renders {item.count} which
leaves the area blank when count is undefined; update the rendering in the
evaluation-section component (the span with className "font-designer-14b
shrink-0 tabular-nums" that references item.count inside the mapping over item)
to display a fallback of 0 when item.count is null/undefined (use a
nullish-coalescing fallback like item.count ?? 0 or equivalent) so the UI shows
0 instead of an empty value.
- Line 30: Replace the forbidden Tailwind arbitrary value in the
evaluation-section component by swapping the className token min-h-[280px] with
the project token min-h-280 on the container div (the div with
className="rounded-100 border-border-subtle min-h-[280px] border p-200") in
_components/evaluation-section.tsx so it uses the defined utility instead of an
arbitrary value.
In `@src/components/pages/group-study-list-page.tsx`:
- Line 11: Import 'StudyListToolbar' is failing because the module
'@/components/pages/study-list-toolbar' doesn't exist; either add the missing
StudyListToolbar component file to the PR or update the import in
group-study-list-page.tsx to the correct path or filename used elsewhere (e.g.,
match the path used by premium-study-list-page.tsx or the actual component
name). Locate the import line referencing StudyListToolbar and fix it by adding
the component file to the repo or changing the import specifier to the existing
module export name/path so the module resolves.
In `@src/components/pages/premium-study-list-page.tsx`:
- Line 68: The div uses a Tailwind arbitrary width value (`max-w-[1280px]`)
which violates the guideline; update the JSX where the element has className
"mx-auto w-full max-w-[1280px] px-400 py-600" to replace the arbitrary token
with the design token `max-w-7xl` (i.e., change `max-w-[1280px]` → `max-w-7xl`),
verify there are no other arbitrary Tailwind values in the same component
(search for `[...]` patterns), and run your formatter/type-checker to ensure the
change compiles cleanly.
- Line 11: The import for StudyListToolbar
('@/components/pages/study-list-toolbar') is missing and causes build
failures—create a new component file that exports a default StudyListToolbar
React component (same named export used by premium-study-list-page.tsx and
group-study-list-page.tsx) and implement the expected props/interface used by
those pages so both imports resolve; also replace the arbitrary Tailwind utility
"max-w-[1280px]" in premium-study-list-page.tsx with the design-token class
"max-w-7xl" to conform to project styling.
In `@src/hooks/common/use-group-study-review-reminder.ts`:
- Around line 69-71: The allLoaded check currently only verifies
participantStudies.length and r.data !== undefined, which ignores query error
states; update the allLoaded computation (referencing allLoaded,
participantStudies, and reviewWrittenResults) to also ensure each review query
is not in error (e.g., !r.isError or r.isError === false) or isSuccess before
treating its data as loaded—for example require reviewWrittenResults.every(r =>
r.isSuccess && r.data !== undefined) or reviewWrittenResults.every(r =>
!r.isError && r.data !== undefined) so a failed query doesn't permanently block
the modal opening.
---
Duplicate comments:
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx:
- Around line 26-27: The visible calculation wrongly ignores showToggle: update
the logic that sets visible (currently using expanded, items,
DEFAULT_SHOW_COUNT) to respect showToggle so that when showToggle is false all
items are shown; modify the expression that computes visible (and adjust hasMore
if needed) to branch on showToggle (e.g., if !showToggle then visible = items
and hasMore = false, otherwise preserve the existing expanded / slice behavior).
---
Nitpick comments:
In `@src/hooks/common/use-group-study-review-reminder.ts`:
- Around line 73-78: useEffect currently depends on the object pendingStudy
which is recreated each render; change the dependency to a stable primitive
(e.g., pendingStudy?.studyId) so the effect only runs when the actual study
changes: update the dependency array in the useEffect that checks pendingStudy,
allLoaded, hasAutoOpenedRef and calls setShowReviewModal to use
pendingStudy?.studyId (and allLoaded) instead of pendingStudy to avoid
unnecessary effect runs while keeping the hasAutoOpenedRef protection.
- Around line 80-89: The current use of type assertions on reviewDetailInfo and
reviewBasicInfo (casting to GroupStudyDetailInfoResponseDto /
GroupStudyBasicInfoResponseDto) is unsafe; instead create explicit, narrower
shapes using Pick or a local interface and construct objects with only the
fields you actually use from pendingStudy (e.g., map title, startDate, endDate),
or type them as Partial/ Pick<...> to reflect the subset; update the code that
defines reviewDetailInfo and reviewBasicInfo to build plain objects from
pendingStudy without "as" assertions and reference the new narrower type (or
Partial/ Pick) so TypeScript enforces the correct fields at compile time.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: bd8d4f73-7502-45d3-a4d3-89683ab8f5df
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (5)
package.jsonsrc/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsxsrc/components/pages/group-study-list-page.tsxsrc/components/pages/premium-study-list-page.tsxsrc/hooks/common/use-group-study-review-reminder.ts
✅ Files skipped from review due to trivial changes (1)
- package.json
| const hasMore = items.length > DEFAULT_SHOW_COUNT; | ||
|
|
||
| return ( | ||
| <div className="rounded-100 border-border-subtle min-h-[280px] border p-200"> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n "src/app/(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx" | sed -n '25,35p'Repository: code-zero-to-one/study-platform-client
Length of output: 590
🏁 Script executed:
cat "src/app/global.css" | grep -E "(--height|--min-height|280|1120)" | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 175
🏁 Script executed:
# Search for any existing uses of min-height in the codebase and their token values
rg "min-h-\[" --type tsx --type ts -A 2 -B 2 | head -50Repository: code-zero-to-one/study-platform-client
Length of output: 109
🏁 Script executed:
# Check if there are predefined height tokens in the project
rg "min-h-" "src/app/global.css" -A 1 -B 1Repository: code-zero-to-one/study-platform-client
Length of output: 216
min-h-[280px]를 프로젝트 토큰 min-h-280으로 교체하세요.
금지된 Tailwind arbitrary value를 사용 중입니다. 프로젝트에서 정의한 min-h-280 유틸리티를 사용해 주세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx
at line 30, Replace the forbidden Tailwind arbitrary value in the
evaluation-section component by swapping the className token min-h-[280px] with
the project token min-h-280 on the container div (the div with
className="rounded-100 border-border-subtle min-h-[280px] border p-200") in
_components/evaluation-section.tsx so it uses the defined utility instead of an
arbitrary value.
| <span className="font-designer-14b shrink-0 tabular-nums"> | ||
| {item.count} | ||
| </span> |
There was a problem hiding this comment.
count가 없을 때 빈 값 대신 0을 렌더링하세요.
Line 60은 item.count가 없으면 숫자 영역이 비어 보일 수 있습니다.
수정 예시
- <span className="font-designer-14b shrink-0 tabular-nums">
- {item.count}
- </span>
+ <span className="font-designer-14b shrink-0 tabular-nums">
+ {item.count ?? 0}
+ </span>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <span className="font-designer-14b shrink-0 tabular-nums"> | |
| {item.count} | |
| </span> | |
| <span className="font-designer-14b shrink-0 tabular-nums"> | |
| {item.count ?? 0} | |
| </span> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/app/`(service)/(my)/my-study-review/group/[groupStudyId]/_components/evaluation-section.tsx
around lines 59 - 61, The numeric span currently renders {item.count} which
leaves the area blank when count is undefined; update the rendering in the
evaluation-section component (the span with className "font-designer-14b
shrink-0 tabular-nums" that references item.count inside the mapping over item)
to display a fallback of 0 when item.count is null/undefined (use a
nullish-coalescing fallback like item.count ?? 0 or equivalent) so the UI shows
0 instead of an empty value.
| import { useStudyListFilter } from '@/hooks/common/use-study-list-filter'; | ||
| import GroupStudyList from '../lists/group-study-list'; | ||
| import MyParticipatingStudiesSection from '../section/my-participating-studies-section'; | ||
| import StudyListToolbar from '@/components/pages/study-list-toolbar'; |
There was a problem hiding this comment.
빌드 실패: 존재하지 않는 모듈 import
premium-study-list-page.tsx와 동일하게 @/components/pages/study-list-toolbar 모듈을 찾을 수 없습니다. PR에 StudyListToolbar 컴포넌트 파일을 포함시키거나 경로를 수정해 주세요.
🧰 Tools
🪛 GitHub Check: typecheck
[failure] 11-11:
Cannot find module '@/components/pages/study-list-toolbar' or its corresponding type declarations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/group-study-list-page.tsx` at line 11, Import
'StudyListToolbar' is failing because the module
'@/components/pages/study-list-toolbar' doesn't exist; either add the missing
StudyListToolbar component file to the PR or update the import in
group-study-list-page.tsx to the correct path or filename used elsewhere (e.g.,
match the path used by premium-study-list-page.tsx or the actual component
name). Locate the import line referencing StudyListToolbar and fix it by adding
the component file to the repo or changing the import specifier to the existing
module export name/path so the module resolves.
| import { useGroupStudyReviewReminder } from '@/hooks/common/use-group-study-review-reminder'; | ||
| import { useStudyListFilter } from '@/hooks/common/use-study-list-filter'; | ||
| import MyParticipatingStudiesSection from '../section/my-participating-studies-section'; | ||
| import StudyListToolbar from '@/components/pages/study-list-toolbar'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: StudyListToolbar 파일 존재 여부 확인
# Expected: 파일이 존재해야 함
fd -t f "study-list-toolbar" --extension ts --extension tsxRepository: code-zero-to-one/study-platform-client
Length of output: 1811
🏁 Script executed:
#!/bin/bash
# StudyListToolbar 파일 검색
# 모든 가능한 파일명 패턴 확인
find . -type f \( -name "*study-list-toolbar*" -o -name "*study_list_toolbar*" -o -name "*studyListToolbar*" \) 2>/dev/null | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# 컴포넌트 디렉토리 구조 확인
ls -la src/components/pages/ 2>/dev/null || echo "디렉토리 없음"Repository: code-zero-to-one/study-platform-client
Length of output: 794
🏁 Script executed:
#!/bin/bash
# 유사한 이름의 파일 검색
find src/components -type f \( -name "*.tsx" -o -name "*.ts" \) 2>/dev/null | grep -i "toolbar\|study" | head -20Repository: code-zero-to-one/study-platform-client
Length of output: 1123
🏁 Script executed:
#!/bin/bash
# premium-study-list-page.tsx 파일 내용 확인
head -50 src/components/pages/premium-study-list-page.tsx | cat -nRepository: code-zero-to-one/study-platform-client
Length of output: 1991
🏁 Script executed:
#!/bin/bash
# study-list-toolbar 유사 파일 찾기
find src -type f \( -name "*.tsx" -o -name "*.ts" \) | xargs grep -l "StudyListToolbar" 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 177
🏁 Script executed:
#!/bin/bash
# toolbar 관련 모든 파일 검색
find src/components -type f \( -name "*toolbar*" \) 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# StudyListToolbar 컴포넌트가 export되는 곳 찾기
rg "export.*StudyListToolbar" --type ts --type tsxRepository: code-zero-to-one/study-platform-client
Length of output: 109
🏁 Script executed:
#!/bin/bash
# group-study-list-page.tsx에서 어떻게 import하는지 확인
grep -n "StudyListToolbar" src/components/pages/group-study-list-page.tsxRepository: code-zero-to-one/study-platform-client
Length of output: 181
🏁 Script executed:
#!/bin/bash
# study-list-toolbar가 정말 없는지 전체 저장소 확인
find . -name "study-list-toolbar*" -o -name "*StudyListToolbar*" 2>/dev/nullRepository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# group-study-list-page.tsx 파일 확인 - StudyListToolbar 사용하는지 확인
head -100 src/components/pages/group-study-list-page.tsx | cat -nRepository: code-zero-to-one/study-platform-client
Length of output: 3707
🏁 Script executed:
#!/bin/bash
# pages 디렉토리의 모든 파일 나열
ls -la src/components/pages/Repository: code-zero-to-one/study-platform-client
Length of output: 794
🏁 Script executed:
#!/bin/bash
# 더 넓은 범위로 toolbar 검색
find src -type f \( -name "*.tsx" -o -name "*.ts" \) -exec grep -l "StudyListToolbar" {} \;Repository: code-zero-to-one/study-platform-client
Length of output: 177
🏁 Script executed:
#!/bin/bash
# section 디렉토리 확인 (import 경로에 ../section이 있음)
ls -la src/components/section/ 2>/dev/null || echo "section 디렉토리 확인"Repository: code-zero-to-one/study-platform-client
Length of output: 671
🏁 Script executed:
#!/bin/bash
# premium-study-list-page.tsx 전체 내용 확인 (특히 line 68 주변)
cat -n src/components/pages/premium-study-list-page.tsx | sed -n '60,80p'Repository: code-zero-to-one/study-platform-client
Length of output: 974
🏁 Script executed:
#!/bin/bash
# components 구조 확인 - 어디에 StudyListToolbar가 있어야 하는지
find src/components -type d | head -30Repository: code-zero-to-one/study-platform-client
Length of output: 983
🏁 Script executed:
#!/bin/bash
# section 디렉토리에서 StudyListToolbar 검색
grep -r "StudyListToolbar" src/components/section/ 2>/dev/null || echo "section에 없음"Repository: code-zero-to-one/study-platform-client
Length of output: 93
🏁 Script executed:
#!/bin/bash
# 전체 components에서 StudyListToolbar 정의 찾기
find src/components -type f \( -name "*.tsx" -o -name "*.ts" \) -exec grep -l "export.*StudyListToolbar\|export default.*StudyListToolbar" {} \;Repository: code-zero-to-one/study-platform-client
Length of output: 64
🏁 Script executed:
#!/bin/bash
# 혹시 다른 이름으로 정의되었을 가능성
find src/components -type f \( -name "*.tsx" -o -name "*.ts" \) -exec grep -l "class StudyListToolbar\|function StudyListToolbar\|const StudyListToolbar" {} \;Repository: code-zero-to-one/study-platform-client
Length of output: 64
필수 파일 누락: StudyListToolbar 컴포넌트가 존재하지 않음
@/components/pages/study-list-toolbar 모듈이 저장소에 없어서 빌드가 실패합니다. 이 컴포넌트는 group-study-list-page.tsx와 premium-study-list-page.tsx 두 페이지 모두에서 import되어 사용 중입니다.
또한 line 68의 max-w-[1280px]는 임의의 Tailwind 값 사용이므로 프로젝트 디자인 토큰인 max-w-7xl로 변경해야 합니다.
🧰 Tools
🪛 GitHub Check: typecheck
[failure] 11-11:
Cannot find module '@/components/pages/study-list-toolbar' or its corresponding type declarations.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/premium-study-list-page.tsx` at line 11, The import for
StudyListToolbar ('@/components/pages/study-list-toolbar') is missing and causes
build failures—create a new component file that exports a default
StudyListToolbar React component (same named export used by
premium-study-list-page.tsx and group-study-list-page.tsx) and implement the
expected props/interface used by those pages so both imports resolve; also
replace the arbitrary Tailwind utility "max-w-[1280px]" in
premium-study-list-page.tsx with the design-token class "max-w-7xl" to conform
to project styling.
|
|
||
| return ( | ||
| <div className="mx-auto w-[1280px] px-400 py-600"> | ||
| <div className="mx-auto w-full max-w-[1280px] px-400 py-600"> |
There was a problem hiding this comment.
Tailwind arbitrary value 대신 디자인 토큰 사용
max-w-[1280px]는 코딩 가이드라인에서 금지된 임의 값(arbitrary value)입니다. 프로젝트에서 w-7xl이 1280px에 해당하는 유효한 토큰이므로 max-w-7xl을 사용해야 합니다.
As per coding guidelines: "Forbidden: Tailwind arbitrary values (e.g., p-[4px], w-[320px]). Use only project custom design tokens."
🔧 디자인 토큰으로 수정
- <div className="mx-auto w-full max-w-[1280px] px-400 py-600">
+ <div className="mx-auto w-full max-w-7xl px-400 py-600">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="mx-auto w-full max-w-[1280px] px-400 py-600"> | |
| <div className="mx-auto w-full max-w-7xl px-400 py-600"> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/pages/premium-study-list-page.tsx` at line 68, The div uses a
Tailwind arbitrary width value (`max-w-[1280px]`) which violates the guideline;
update the JSX where the element has className "mx-auto w-full max-w-[1280px]
px-400 py-600" to replace the arbitrary token with the design token `max-w-7xl`
(i.e., change `max-w-[1280px]` → `max-w-7xl`), verify there are no other
arbitrary Tailwind values in the same component (search for `[...]` patterns),
and run your formatter/type-checker to ensure the change compiles cleanly.
| const allLoaded = | ||
| participantStudies.length > 0 && | ||
| reviewWrittenResults.every((r) => r.data !== undefined); |
There was a problem hiding this comment.
allLoaded 체크가 쿼리 에러 상태를 고려하지 않음
r.data !== undefined만 확인하면 쿼리가 에러로 실패한 경우에도 data가 undefined이므로 allLoaded가 영원히 false가 됩니다. 이로 인해 하나의 스터디 리뷰 작성 여부 조회가 실패하면 모달이 절대 열리지 않는 문제가 발생할 수 있습니다.
🛠️ 에러 상태를 포함한 수정 제안
const allLoaded =
participantStudies.length > 0 &&
- reviewWrittenResults.every((r) => r.data !== undefined);
+ reviewWrittenResults.every((r) => r.isSuccess || r.isError);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const allLoaded = | |
| participantStudies.length > 0 && | |
| reviewWrittenResults.every((r) => r.data !== undefined); | |
| const allLoaded = | |
| participantStudies.length > 0 && | |
| reviewWrittenResults.every((r) => r.isSuccess || r.isError); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/hooks/common/use-group-study-review-reminder.ts` around lines 69 - 71,
The allLoaded check currently only verifies participantStudies.length and r.data
!== undefined, which ignores query error states; update the allLoaded
computation (referencing allLoaded, participantStudies, and
reviewWrittenResults) to also ensure each review query is not in error (e.g.,
!r.isError or r.isError === false) or isSuccess before treating its data as
loaded—for example require reviewWrittenResults.every(r => r.isSuccess && r.data
!== undefined) or reviewWrittenResults.every(r => !r.isError && r.data !==
undefined) so a failed query doesn't permanently block the modal opening.
🌱 연관된 이슈
☘️ 작업 내용
🍀 참고사항
스크린샷 (선택)
Summary by CodeRabbit
New Features
Bug Fixes